본문으로 건너뛰기

23.07.20

오늘 한 일

  • Gloddy 개발 - 게시글 작성 페이지 구현
  • CS 스터디 진행

하루 요약

  • 13:30 ~ 18:00 / 19:00 ~ 21:00 카공

Gloddy 개발 - 게시글 작성 페이지 구현

저번 전역 상태에 대한 고민을 해보고, 전역 상태를 사용하지 않고 특정 범위에 국한된 상태는 그 범위 내에서 관리하기 위해 Provider로 묶어서 관리해보기로 했다.

일단 최종 구현은 다음과 같다.

TopNavigationBar에서 이미지 추가 버튼이 존재한다.


Page 컴포넌트에서는 다음과 같이 구성된다.

<main>
<WriteTopNavigationBar />
<InputForm />
</main>
  • InputForm 컴포넌트에 TNB를 넣는다 ❌
    • InputForm은 form 만 들고 있어야 함
  • page 컴포넌트에서 상태를 관리하고, InputForm에 props로 넘겨준다 ❌
    • page 컴포넌트는 서버 컴포넌트이기 때문에 상태를 가질 수 없다. (페이지 컴포넌트는 RSC로 하자고 컨벤션 맞춤)
  • Context API를 사용해서 상태를 공유한다 🤔
  • 전역 상태 라이브러리를 사용해서 상태를 공유한다 🤔

전역 상태라고 보기엔 애매하다. 게시글 작성 페이지에서만 사용하기 때문에 특정 범위에 국한된 상태라고 보는게 맞다.

그래서 Context API를 사용해서 상태를 공유하기로 했다.


Context API 사용

export default function WritePage() {
return (
<main>
<WriteContextProvider>
<WriteTopNavigationBar />
<InputForm />
</WriteContextProvider>
</main>
);
}

WriteContextProvider 컴포넌트를 만들어보았다.

// WriteContext.tsx

'use client';
import { createContext, useContext, useMemo, useState } from 'react';

import type { ImageType, StrictPropsWithChildren } from '@/types';

type WriteContextType = {
images: ImageType[];
setImages: React.Dispatch<React.SetStateAction<ImageType[]>>;
};

const WriteContext = createContext<WriteContextType | null>(null);

function WriteContextProvider({ children }: StrictPropsWithChildren) {
const [images, setImages] = useState<ImageType[]>([]);

const contextValue = useMemo(() => ({ images, setImages }), [images]);

return <WriteContext.Provider value={contextValue}>{children}</WriteContext.Provider>;
}

function useWriteContext() {
const context = useContext(WriteContext);
if (context === null) {
throw new Error('useWriteContext must be used within a WriteContextProvider');
}

return context;
}

export { useWriteContext, WriteContextProvider };

뭔가 복잡해 보이지만 사실 별거 없다.

  1. 먼저 WriteContext를 만들어준다.
  2. WriteContextProvider 컴포넌트를 만들어준다.
    • WriteContextvalueimagessetImages를 넘겨준다.
    • 이 때, contextValue는 useMemo를 사용해서 최적화를 해준다.
  3. useWriteContext를 만들어준다.
    • WriteContext를 사용할 때, useContext를 사용해서 context를 가져온다.
    • 만약 contextnull이라면, useWriteContext를 사용한 컴포넌트가 WriteContextProvider 컴포넌트 내부에 존재하지 않는다는 것이다.
    • 그래서 에러를 던져준다.
    • contextnull이 아니라면, context를 반환해준다.

만약 위에서 3번 처리를 해주지 않는다면(타입을 좁혀주지 않는다면), context를 사용할 때 타입스크립트에서 null일 수도 있다고 생각하기 때문에 null 체크를 해줘야한다.

context 사용

const { images, setImages } = useWriteContext();

이렇게 사용하고 싶은 곳에서 useWriteContext를 사용해서 imagessetImages를 가져온다.

하지만, Provider로 감싸진 곳에 선언된 컴포넌트에서만 사용할 수 있다.

이렇게 지역적으로 사용할 수 있는 상태를 만들어줄 수 있다.

만약 전역 상태 라이브러리를 사용했다면?

전역 상태 라이브러리를 사용했더라도 동일하게 로직을 구현할 수 있었을 것이다.

하지만, 다른 곳에서도 해당 상태를 사용할 수 있기 때문에, 전역으로 상태를 빼는 건 조심스럽게 결정해야 한다고 생각한다.

위 처럼 상태를 사용할 수 있는 범위를 정해서 사용하는게 더 좋은 것 같다.

Jotai도 비슷한 생각을 했다

Jotai컨셉 문서를 읽어보면 Context의 리렌더링 문제에서 출발한 것 같다.

이 라이브러리가 나오기 전 use-context-selector로 문제를 해결하려고 했었다.

그리고 Jotai의 전신인 use-atom도 있었다.

Jotai에서 Provider라는 것도 제공해준다.

Provider

The Provider component is to provide state for a component sub tree. Multiple Providers can be used for multiple subtrees, and they can even be nested. This works just like React Context.

If an atom is used in a tree without a Provider, it will use the default state. This is so-called provider-less mode.

Providers are useful for three reasons:

  1. To provide a different state for each sub tree.
  2. To accept initial values of atoms.
  3. To clear all atoms by remounting.

선택적으로 store를 사용해서 특정 범위 내에서 상태를 관리할 수 있다.

상황에 따라 전역 상태로도, 특정 범위 내에서만 상태를 관리할 수도 있다.


... 다음에 기회가 되면 한 번 써봐야겠다.


내일 할 일

  • Gloddy 상호평가 페이지 구현
  • Rust 공부
  • GDG 회의 8시